`timescale 1ns / 1ps
/*--------------------------------------------------------------------*\
FileName        : hpm_spram.v
Author          : hpy
Email           : yuan_hp@qq.com
Date            : 2025.06.26
Description     : 多平台spram


1.例化示例 
hpm_spram #(
    .PLAT(""),            // platform  "quartus" "vivado" "lattice"  none:verilog 
    .INIT_MEM_FILE(""),   // 初始化文件 *.mem文件
    .DATA_NUM(8),         // 数据存储个数
    .ADDR_WIDTH(3),       // 地址位宽
    .DATA_WIDTH(8),       // 输入数据位宽
	.OUTPUT_REG(1)        // 输出是否有reg，1有，0没有，默认1
) u_hpm_spram (
    .address(),  // input[ADDR_WIDTH-1:0]
    .clock(),    // input[0:0] 
    .data(),     // input[DATA_WIDTH-1:0]
    .wren(),     // input[0:0] 
    .q()         // output[DATA_WIDTH-1:0]
);
\*--------------------------------------------------------------------*/
 
module hpm_spram #(
    parameter  PLAT = "quartus", // platform-> "quartus" "vivado" "lattice"
    parameter  INIT_MEM_FILE = "init.mem", // memory init file , *.mem 文件
    parameter  DATA_NUM = 256 ,
    parameter  ADDR_WIDTH = 8,
    parameter  DATA_WIDTH = 8,
	parameter  OUTPUT_REG = 1
)(
	address,
	clock,
	data,
	wren,
	q
);



input	[ADDR_WIDTH-1:0]  address;
input	  clock;
input	[DATA_WIDTH-1:0]  data;
input	  wren;
output	[DATA_WIDTH-1:0]  q;


localparam Q_REG = |OUTPUT_REG ? "CLOCK0" : "UNREGISTERED"  ;
localparam READ_LATENCY = |OUTPUT_REG ? 1 : 0;
localparam pmi_regmode = |OUTPUT_REG ? "reg":"noreg" ;

generate
// -------------------------------------------------  quartus -------------------------------------------------
    if(PLAT == "quartus") begin
        wire [DATA_WIDTH-1:0] sub_wire0;
        assign q = sub_wire0[DATA_WIDTH-1:0];
 
        altsyncram	altsyncram_component (
                    .address_a (address),
                    .clock0 (clock),
                    .data_a (data),
                    .wren_a (wren),
                    .q_a (sub_wire0),
                    .aclr0 (1'b0),
                    .aclr1 (1'b0),
                    .address_b (1'b1),
                    .addressstall_a (1'b0),
                    .addressstall_b (1'b0),
                    .byteena_a (1'b1),
                    .byteena_b (1'b1),
                    .clock1 (1'b1),
                    .clocken0 (1'b1),
                    .clocken1 (1'b1),
                    .clocken2 (1'b1),
                    .clocken3 (1'b1),
                    .data_b (1'b1),
                    .eccstatus (),
                    .q_b (),
                    .rden_a (1'b1),
                    .rden_b (1'b1),
                    .wren_b (1'b0));
        defparam
            altsyncram_component.clock_enable_input_a = "BYPASS",
            altsyncram_component.clock_enable_output_a = "BYPASS",
            altsyncram_component.init_file = INIT_MEM_FILE ,
            altsyncram_component.intended_device_family = "Cyclone IV E",
            altsyncram_component.lpm_hint = "ENABLE_RUNTIME_MOD=NO",
            altsyncram_component.lpm_type = "altsyncram",
            altsyncram_component.numwords_a = DATA_NUM,
            altsyncram_component.operation_mode = "SINGLE_PORT",
            altsyncram_component.outdata_aclr_a = "NONE",
            altsyncram_component.outdata_reg_a = Q_REG,
            altsyncram_component.power_up_uninitialized = "FALSE",
            altsyncram_component.read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ",
            altsyncram_component.widthad_a = ADDR_WIDTH,
            altsyncram_component.width_a = DATA_WIDTH,
			altsyncram_component.init_file = INIT_MEM_FILE,
            altsyncram_component.width_byteena_a = 1;
    end else if(PLAT=="vivado") begin 
        // xpm_memory_spram: Single Port RAM
        // Xilinx Parameterized Macro, version 2018.3

        xpm_memory_spram #(
            .ADDR_WIDTH_A(ADDR_WIDTH),              // DECIMAL
            .AUTO_SLEEP_TIME(0),           // DECIMAL
            .BYTE_WRITE_WIDTH_A(DATA_WIDTH),       // DECIMAL
            .ECC_MODE("no_ecc"),           // String
            .MEMORY_INIT_FILE(|INIT_MEM_FILE ? INIT_MEM_FILE : "none"),     // String
            .MEMORY_INIT_PARAM("0"),       // String
            .MEMORY_OPTIMIZATION("true"),  // String
            .MEMORY_PRIMITIVE("auto"),     // String
            .MEMORY_SIZE(DATA_NUM*DATA_WIDTH),            // DECIMAL
            .MESSAGE_CONTROL(0),           // DECIMAL
            .READ_DATA_WIDTH_A(DATA_WIDTH),        // DECIMAL
            .READ_LATENCY_A(READ_LATENCY),            // DECIMAL
            .READ_RESET_VALUE_A("0"),      // String
            .RST_MODE_A("SYNC"),           // String
            .USE_MEM_INIT(1),              // DECIMAL
            .WAKEUP_TIME("disable_sleep"), // String
            .WRITE_DATA_WIDTH_A(DATA_WIDTH),       // DECIMAL
            .WRITE_MODE_A("read_first")    // String
        )
        xpm_memory_spram_inst (
            .dbiterra(),             // 1-bit output: Status signal to indicate double bit error occurrence
                                            // on the data output of port A.

            .douta(q),                   // READ_DATA_WIDTH_A-bit output: Data output for port A read operations.
            .sbiterra(sbiterra),             // 1-bit output: Status signal to indicate single bit error occurrence
                                            // on the data output of port A.

            .addra(address),                   // ADDR_WIDTH_A-bit input: Address for port A write and read operations.
            .clka(clock),                     // 1-bit input: Clock signal for port A.
            .dina(data),                     // WRITE_DATA_WIDTH_A-bit input: Data input for port A write operations.
            .ena(1'b1),                       // 1-bit input: Memory enable signal for port A. Must be high on clock
                                            // cycles when read or write operations are initiated. Pipelined
                                            // internally.

            .injectdbiterra(1'b1), // 1-bit input: Controls double bit error injection on input data when
                                            // ECC enabled (Error injection capability is not available in
                                            // "decode_only" mode).

            .injectsbiterra(1'b1), // 1-bit input: Controls single bit error injection on input data when
                                            // ECC enabled (Error injection capability is not available in
                                            // "decode_only" mode).

            .regcea(1'b1),                 // 1-bit input: Clock Enable for the last register stage on the output
                                            // data path.

            .rsta(1'b0),                     // 1-bit input: Reset signal for the final port A output register stage.
                                            // Synchronously resets output port douta to the value specified by
                                            // parameter READ_RESET_VALUE_A.

            .sleep(sleep),                   // 1-bit input: sleep signal to enable the dynamic power saving feature.
            .wea(wren)                        // WRITE_DATA_WIDTH_A-bit input: Write enable vector for port A input
                                            // data port dina. 1 bit wide when word-wide writes are used. In
                                            // byte-wide write configurations, each bit controls the writing one
                                            // byte of dina to address addra. For example, to synchronously write
                                            // only bits [15-8] of dina when WRITE_DATA_WIDTH_A is 32, wea would be
                                            // 4'b0010.

        );
    end else if(PLAT=="lattice") begin
        // LATTICE USING PMI  
        pmi_ram_dp #(
        .pmi_wr_addr_depth(DATA_NUM),
        .pmi_wr_addr_width(ADDR_WIDTH),
        .pmi_wr_data_width(DATA_WIDTH),
        .pmi_rd_addr_depth(DATA_NUM),
        .pmi_rd_addr_width(ADDR_WIDTH),
        .pmi_rd_data_width(DATA_WIDTH),
        .pmi_regmode (pmi_regmode),
        .pmi_gsr ("disable"),
        .pmi_resetmode ("sync"),
        .pmi_optimization ("speed"),
        .pmi_init_file (|INIT_MEM_FILE? INIT_MEM_FILE : "none"),
        .pmi_init_file_format ("hex"),
        .pmi_family ("EC"),
        .module_type ("pmi_ram_dp")
        )
        u_pmi_ram_dp (
            .Data(data),
            .WrAddress(address),
            .RdAddress(address),
            .WrClock(clock),
            .RdClock(clock),
            .WrClockEn(1'b1),
            .RdClockEn(1'b0),
            .WE(wren),
            .Reset(1'b0),
            .Q(q)
        ) /*synthesis syn_black_box*/;	
    end else begin
        (* ram_style="distributed" *) reg [DATA_WIDTH-1:0] mem [0:DATA_NUM-1] /* synthesis syn_ramstyle = "block_ram" */;
        reg [DATA_WIDTH - 1:0] q_r ;
        assign q = q_r ;

        initial begin
            if(|INIT_MEM_FILE) begin
                $readmemh(INIT_MEM_FILE,mem);
            end
        end

        always @(posedge clock) begin
            if (wren) begin
                mem[address] <= data;
            end
        end 

        if(|OUTPUT_REG) begin
            always @(posedge clock) begin
                q_r <= mem[address];
            end
        end else begin
            always@(address,clock) begin
                q_r = mem[address];
            end
        end
    end
endgenerate 
 
endmodule
 
